home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / GRAPHICS / COMMON.ZIP / XSCALE.ASM < prev    next >
Encoding:
Assembly Source File  |  1994-09-15  |  23.5 KB  |  484 lines

  1. ;=========================================================================
  2. ; XSCALE1.ASM by John A. Slagel, jas37876@uxa.cso.uiuc.edu
  3. ; This is some code to do bitmap scaling in VGA Mode X.  It can scale a
  4. ; bitmap of any size down to 2 pixels wide, or up to thousands of pixels
  5. ; wide.  It performs complete clipping, with only a small constant amount
  6. ; of time to clip, no matter how huge the image is.  It draws column by
  7. ; column to reduce the number of plane switches, which are slow. The inner
  8. ; column loop has been optimized for no memory accesses, except to read or
  9. ; write a pixel.  This uses MASM 5.1 features, and can be compiled in any
  10. ; memory model by changing the .MODEL line, but make sure that you always
  11. ; pass a far pointer to the bitmap data, regardless of memory model.
  12. ; C-callable as:
  13. ;   void xscale( int X, int Y, int DW, int DY,
  14. ;                     int SW, int SH, int offs, void far * Bitmap );
  15. ;
  16. ;   void xscale_masked( int X, int Y, int DW, int DY,
  17. ;                       int SW, int SH, int offs, void far * Bitmap );
  18. ;
  19. ; X,Y   are the upper left-hand coordinates of where to draw the bitmap.
  20. ; DW,DH are the width and height of the SCALEed bitmap
  21. ; SW,SH are the width and height of the source bitmap.
  22. ; Bitmap is a pointer to the bitmap bits.
  23. ;
  24. ; minor mods by AJR Aug 1994 to make it work with XLIB's addressing.
  25. ; xscale_masked() added by AJR Sept 1994, only had to add two lines
  26. ; to do masked scaling. Assembled just fine with TASM 3.1
  27. ; Alexander J. Russell   Internet: alexad3@icebox.iceonline.com
  28. ;
  29. ; NOTE:
  30. ; The bitnap pointer points to a plain linear bitmap as used for mode13.
  31. ;
  32. ;==========================================================================
  33. include xlib.inc
  34. include xscale.inc
  35.  
  36. SC_INDEX2   EQU 03C5h               ; Port number of VGA Seqeuncer Data
  37.  
  38. .DATA
  39.  
  40.  
  41. ClipLt      DW  0                   ; Left clipping boundry
  42. ClipRt      DW  359                 ; Right clipping boundry
  43. ClipTp      DW  0                   ; Top clipping boundry
  44. ClipBt      DW  239                 ; Bottom clipping boundry
  45.  
  46. .CODE
  47.  
  48. xscale PROC DestX:WORD, DestY:WORD,                 \
  49.             DestWidth:WORD, DestHeight:WORD,        \
  50.             SourceWidth:WORD, SourceHeight:WORD,    \
  51.             ScrnOffs:WORD, \
  52.             Bitmap:FAR PTR
  53. LOCAL   DecisionX:WORD, DecisionY:WORD, ClippedWidth:WORD, ClippedHeight:WORD
  54.  
  55.         push  si
  56.         push  di
  57.     push  ds
  58.  
  59.         cmp     DestWidth, 2        ; If destination width is less than 2
  60.         jl      Done                ;     then don't draw it.
  61.  
  62.         cmp     DestHeight, 2       ; If destination height is less than 2
  63.         jl      Done                ;     then don't draw it.
  64.  
  65.         mov     ax, DestY           ; If it is completely below the
  66.         cmp     ax, ClipBt          ; lower clip bondry,
  67.         jg      Done                ;     then don't draw it.
  68.  
  69.         add     ax, DestHeight      ; If it is above clip boundries
  70.         dec     ax                  ;     then don't draw it.
  71.         cmp     ax, ClipTp
  72.         jl      Done
  73.  
  74.         mov     ax, DestX           ; If it is to the right of the
  75.         mov     cx, ClipRt          ; right clip boundry
  76.         cmp     ax, ClipRt          ;     then don't draw it.
  77.         jg      Done
  78.  
  79.         add     ax, DestWidth       ; If it is completely to the left
  80.         dec     ax                  ; of the left clip boundry,
  81.         cmp     ax, ClipLt          ;     then don't draw it.
  82.         jl      Done
  83.  
  84.         les     si, Bitmap          ; Make DS:SI point to bitmap data
  85.  
  86.         mov     ax, DestWidth       ; ClippedWidth is initially set to
  87.         mov     ClippedWidth, ax    ; the requested dest width.
  88.  
  89.         shl     ax,1                ; Initialize the X decision var
  90.         neg     ax                  ; to be -2*DestWidth
  91.         mov     DecisionX, ax       ;
  92.  
  93.         mov     ax, DestHeight      ; ClippedHeight is initially set to
  94.         mov     ClippedHeight, ax   ; the requested dest size.
  95.  
  96.         shl     ax,1                ; Initialize the Y decision var
  97.         neg     ax                  ; to be -2*DestHeight
  98.         mov     DecisionY, ax       ;
  99.  
  100.         movsx   eax, ClipTp         ; If Y is below the top
  101.         mov     edx, eax            ; clipping boundry, then we don't
  102.         sub     dx, DestY           ; need to clip the top, so we can
  103.         js      NoTopClip           ; jump over the clipping stuff.
  104.  
  105.         mov     DestY, ax           ; This block performs clipping on the
  106.         sub     ClippedHeight, dx   ; top of the bitmap.  I have heavily
  107.         movsx   ecx, SourceHeight   ; optimized this block to use only 4
  108.         imul    ecx, edx            ; 32-bit registers, so I'm not even
  109.         mov     eax, ecx            ; gonna try to explain what it's doing.
  110.         mov     edx, 0              ; But I can tell you what results from
  111.         movsx   ebx, DestHeight     ; this:  The DecisionY var is updated
  112.         idiv    ebx                 ; to start at the right clipped row.
  113.         movsx   edx, SourceWidth    ; Y is moved to the top clip
  114.         imul    edx, eax            ; boundry. ClippedHeight is lowered since
  115.         add     si, dx              ; we won't be drawing all the requested
  116.         imul    eax, ebx            ; rows.  SI is changed to point over
  117.         sub     ecx, eax            ; the bitmap data that is clipped off.
  118.         sub     ecx, ebx            ;
  119.         shl     ecx, 1              ;
  120.         mov     DecisionY, cx       ; <end of top clipping block >
  121.  
  122. NoTopClip:
  123.         mov     ax, DestY           ; If the bitmap doesn't extend over the
  124.         add     ax, ClippedHeight   ; bottom clipping boundry, then we
  125.         dec     ax                  ; don't need to clip the bottom, so we
  126.         cmp     ax, ClipBt          ; can jump over the bottom clip code.
  127.         jle     NoBottomClip        ;
  128.  
  129.         mov     ax, ClipBt          ; Clip off the bottom by reducing the
  130.         sub     ax, DestY           ; ClippedHeight so that the bitmap won't
  131.         inc     ax                  ; extend over the lower clipping
  132.         mov     ClippedHeight, ax   ; boundry.
  133.  
  134. NoBottomClip:
  135.         movsx   eax, ClipLt         ; If X is to the left of the
  136.         mov     edx, eax            ; top clipping boundry, then we don't
  137.         sub     dx, DestX           ; need to clip the left, so we can
  138.         js      NoLeftClip          ; jump over the clipping stuff.
  139.  
  140.         mov     DestX, ax           ; This block performs clipping on the
  141.         sub     ClippedWidth, dx    ; left of the bitmap.  I have heavily
  142.         movsx   ecx, SourceWidth    ; optimized this block to use only 4
  143.         imul    ecx, edx            ; 32-bit registers, so I'm not even
  144.         mov     eax, ecx            ; gonna try to explain what it's doing.
  145.         mov     edx, 0              ; But I can tell you what results from
  146.         movsx   ebx, DestWidth      ; this:  The DecisionX var is updated
  147.         idiv    ebx                 ; to start at the right clipped column.
  148.         add     si, ax              ; X is moved to the left clip
  149.         imul    eax, ebx            ; boundry. ClippedWidth is reduced since
  150.         sub     ecx, eax            ; we won't be drawing all the requested
  151.         sub     ecx, ebx            ; cols.  SI is changed to point over
  152.         shl     ecx, 1              ; the bitmap data that is clipped off.
  153.         mov     DecisionX, cx       ; <end of left clipping block >
  154.  
  155. NoLeftClip:
  156.         mov     ax, DestX           ; If the bitmap doesn't extend over the
  157.         add     ax, ClippedWidth    ; right clipping boundry, then we
  158.         dec     ax                  ; don't need to clip the right, so we
  159.         cmp     ax, ClipRt          ; can jump over the right clip code.
  160.         jle     NoClipRight         ;
  161.  
  162.         mov     ax, ClipRt          ; Clip off the right by reducing the
  163.         sub     ax, DestX           ; ClippedWidth so that the bitmap won't
  164.         inc     ax                  ; extend over the right clipping
  165.         mov     ClippedWidth, ax    ; boundry.
  166.  
  167. NoClipRight:
  168.  
  169.         ; only this little bit here modded by AJR for XLIB addresing
  170.                                     ; Calculate starting video address in
  171.         mov     ax, DestY           ; VGA memory. This code sets DS to the
  172.         mov     bx, ScrnLogicalByteWidth ; (global var, needs DS intact)
  173.         mul     bx                  ; VGA segment, which is usually at
  174.         mov     di, ScrnOffs        ; 0a000
  175.         add     di, ax
  176.         mov     ax, SCREEN_SEG      ; We are going to set DS:DI to point
  177.         mov     ds, ax              ; to the start point in vid mem to start
  178.         mov     ax, DestX           ; drawing at.
  179.         mov     cx, ax              ; The offset DI is calculated by:
  180.         shr     ax, 2               ;     DI = Y*WIDTH+X/4 + offset
  181.         add     di, ax              ; DS:DI is ready!
  182.         ; end of mods
  183.  
  184.         mov     dx, SC_INDEX       ; Point the VGA Sequencer to the Map
  185.         mov     al, MAP_MASK        ; Mask register, so that we only need
  186.         out     dx, al              ; to send out 1 byte per column.
  187.  
  188.         inc     dx                  ; Move to the Sequencer's Data register.
  189.         and     cx, 3               ; Calculate the starting plane. This is
  190.         mov     al, 11h             ; just:
  191.         shl     al, cl              ; Plane =  (11h << (X AND 3))
  192.         out     dx, al              ; Select the first plane.
  193.  
  194.         ALIGN   2                   ; Since this point gets jumped to a lot,
  195.                                     ; make sure that it is DWORD aligned.
  196. RowLoop:
  197.         push    si                  ; Save the starting source index
  198.         push    di                  ; Save the starting dest index
  199.         push    ax                  ; Save the current plane mask
  200.         push    bp                  ; Save the current base pointer
  201.  
  202.         mov     ax, ClippedHeight   ; Use AL for row counter (0-239)
  203.         mov     bx, DecisionY       ; Use BX for decision variable
  204.         mov     cx, SourceWidth     ; Use CX for source width
  205.         mov     dx, SourceHeight    ; Use DX for source height * 2
  206.         shl     dx, 1
  207.         mov     bp, DestHeight      ; Use BP for dest height * 2
  208.         shl     bp, 1
  209.         mov     ah, es:[si]         ; Get the first source pixel
  210.  
  211.         ALIGN   2                   ; Common jump point... align for speed.
  212. ColumnLoop:
  213.         mov     ds:[di], ah         ; Draw a pixel
  214.         dec     al                  ; Decrement line counter
  215.         jz      DoneWithCol         ; See if we're done with this column
  216.         add     di, 90              ; Go on to the next screen row
  217.         add     bx, dx              ; Increment the decision variable
  218.         js      ColumnLoop          ; Draw this source pixel again
  219.  
  220. IncSourceRow:
  221.         add     si, cx              ; Move to the next source pixel
  222.         sub     bx, bp              ; Decrement the decision variable
  223.         jns     IncSourceRow        ; See if we need to skip another source pixel
  224.         mov     ah, es:[si]         ; Get the next source pixel
  225.         jmp     ColumnLoop          ; Start drawing this pixel
  226.  
  227. DoneWithCol:
  228.         pop     bp                  ; Restore BP to access variables
  229.         pop     ax                  ; Restore AL = plane mask
  230.         pop     di                  ; Restore DI to top row of screen
  231.         pop     si                  ; Restore SI to top row of source bits
  232.  
  233.         rol     al, 1               ; Move to next plane
  234.         adc     di, 0               ; Go on to next screen column
  235.         mov     dx, SC_INDEX2       ; Tell the VGA what column we're in
  236.         out     dx, al              ; by updating the map mask register
  237.  
  238.         shl     cx, 1               ; CX = SourceWidth * 2
  239.         mov     bx, DecisionX       ; Use BX for the X decision variable
  240.         add     bx, cx              ; Increment the X decision variable
  241.         js      NextCol             ; Jump if we're still in the same source col.
  242.         mov     dx, DestWidth       ; DX = W * 2
  243.         shl     dx, 1
  244. IncSourceCol:
  245.         inc     si                  ; Move to next source column
  246.         sub     bx, dx              ; Decrement X decision variable
  247.         jns     IncSourceCol        ; See if we skip another source column
  248. NextCol:
  249.         mov     DecisionX, bx       ; Free up BX for ColLoop
  250.         dec     ClippedWidth        ; If we're not at last column
  251.         jnz     RowLoop             ;    then do another column
  252. Done:
  253.         pop   ds                          ; restore data segment
  254.         pop   di                          ; restore registers
  255.         pop   si
  256.  
  257.  
  258.         ret                         ; We're done!
  259.  
  260. xscale  ENDP
  261.  
  262.  
  263.  
  264.  
  265. xscale_masked PROC DestX:WORD, DestY:WORD,                 \
  266.             DestWidth:WORD, DestHeight:WORD,        \
  267.             SourceWidth:WORD, SourceHeight:WORD,    \
  268.             ScrnOffs:WORD, \
  269.             Bitmap:FAR PTR
  270. LOCAL   DecisionX:WORD, DecisionY:WORD, ClippedWidth:WORD, ClippedHeight:WORD
  271.  
  272.         push  si
  273.         push  di
  274.     push  ds
  275.  
  276.         cmp     DestWidth, 2        ; If destination width is less than 2
  277.         jl      m_Done                ;     then don't draw it.
  278.  
  279.         cmp     DestHeight, 2       ; If destination height is less than 2
  280.         jl      m_Done                ;     then don't draw it.
  281.  
  282.         mov     ax, DestY           ; If it is completely below the
  283.         cmp     ax, ClipBt          ; lower clip bondry,
  284.         jg      m_Done                ;     then don't draw it.
  285.  
  286.         add     ax, DestHeight      ; If it is above clip boundries
  287.         dec     ax                  ;     then don't draw it.
  288.         cmp     ax, ClipTp
  289.         jl      m_Done
  290.  
  291.         mov     ax, DestX           ; If it is to the right of the
  292.         mov     cx, ClipRt          ; right clip boundry
  293.         cmp     ax, ClipRt          ;     then don't draw it.
  294.         jg      m_Done
  295.  
  296.         add     ax, DestWidth       ; If it is completely to the left
  297.         dec     ax                  ; of the left clip boundry,
  298.         cmp     ax, ClipLt          ;     then don't draw it.
  299.         jl      m_Done
  300.  
  301.         les     si, Bitmap          ; Make DS:SI point to bitmap data
  302.  
  303.         mov     ax, DestWidth       ; ClippedWidth is initially set to
  304.         mov     ClippedWidth, ax    ; the requested dest width.
  305.  
  306.         shl     ax,1                ; Initialize the X decision var
  307.         neg     ax                  ; to be -2*DestWidth
  308.         mov     DecisionX, ax       ;
  309.  
  310.         mov     ax, DestHeight      ; ClippedHeight is initially set to
  311.         mov     ClippedHeight, ax   ; the requested dest size.
  312.  
  313.         shl     ax,1                ; Initialize the Y decision var
  314.         neg     ax                  ; to be -2*DestHeight
  315.         mov     DecisionY, ax       ;
  316.  
  317.         movsx   eax, ClipTp         ; If Y is below the top
  318.         mov     edx, eax            ; clipping boundry, then we don't
  319.         sub     dx, DestY           ; need to clip the top, so we can
  320.         js      m_NoTopClip           ; jump over the clipping stuff.
  321.  
  322.         mov     DestY, ax           ; This block performs clipping on the
  323.         sub     ClippedHeight, dx   ; top of the bitmap.  I have heavily
  324.         movsx   ecx, SourceHeight   ; optimized this block to use only 4
  325.         imul    ecx, edx            ; 32-bit registers, so I'm not even
  326.         mov     eax, ecx            ; gonna try to explain what it's doing.
  327.         mov     edx, 0              ; But I can tell you what results from
  328.         movsx   ebx, DestHeight     ; this:  The DecisionY var is updated
  329.         idiv    ebx                 ; to start at the right clipped row.
  330.         movsx   edx, SourceWidth    ; Y is moved to the top clip
  331.         imul    edx, eax            ; boundry. ClippedHeight is lowered since
  332.         add     si, dx              ; we won't be drawing all the requested
  333.         imul    eax, ebx            ; rows.  SI is changed to point over
  334.         sub     ecx, eax            ; the bitmap data that is clipped off.
  335.         sub     ecx, ebx            ;
  336.         shl     ecx, 1              ;
  337.         mov     DecisionY, cx       ; <end of top clipping block >
  338.  
  339. m_NoTopClip:
  340.         mov     ax, DestY           ; If the bitmap doesn't extend over the
  341.         add     ax, ClippedHeight   ; bottom clipping boundry, then we
  342.         dec     ax                  ; don't need to clip the bottom, so we
  343.         cmp     ax, ClipBt          ; can jump over the bottom clip code.
  344.         jle     m_NoBottomClip        ;
  345.  
  346.         mov     ax, ClipBt          ; Clip off the bottom by reducing the
  347.         sub     ax, DestY           ; ClippedHeight so that the bitmap won't
  348.         inc     ax                  ; extend over the lower clipping
  349.         mov     ClippedHeight, ax   ; boundry.
  350.  
  351. m_NoBottomClip:
  352.         movsx   eax, ClipLt         ; If X is to the left of the
  353.         mov     edx, eax            ; top clipping boundry, then we don't
  354.         sub     dx, DestX           ; need to clip the left, so we can
  355.         js      m_NoLeftClip          ; jump over the clipping stuff.
  356.  
  357.         mov     DestX, ax           ; This block performs clipping on the
  358.         sub     ClippedWidth, dx    ; left of the bitmap.  I have heavily
  359.         movsx   ecx, SourceWidth    ; optimized this block to use only 4
  360.         imul    ecx, edx            ; 32-bit registers, so I'm not even
  361.         mov     eax, ecx            ; gonna try to explain what it's doing.
  362.         mov     edx, 0              ; But I can tell you what results from
  363.         movsx   ebx, DestWidth      ; this:  The DecisionX var is updated
  364.         idiv    ebx                 ; to start at the right clipped column.
  365.         add     si, ax              ; X is moved to the left clip
  366.         imul    eax, ebx            ; boundry. ClippedWidth is reduced since
  367.         sub     ecx, eax            ; we won't be drawing all the requested
  368.         sub     ecx, ebx            ; cols.  SI is changed to point over
  369.         shl     ecx, 1              ; the bitmap data that is clipped off.
  370.         mov     DecisionX, cx       ; <end of left clipping block >
  371.  
  372. m_NoLeftClip:
  373.         mov     ax, DestX           ; If the bitmap doesn't extend over the
  374.         add     ax, ClippedWidth    ; right clipping boundry, then we
  375.         dec     ax                  ; don't need to clip the right, so we
  376.         cmp     ax, ClipRt          ; can jump over the right clip code.
  377.         jle     m_NoClipRight         ;
  378.  
  379.         mov     ax, ClipRt          ; Clip off the right by reducing the
  380.         sub     ax, DestX           ; ClippedWidth so that the bitmap won't
  381.         inc     ax                  ; extend over the right clipping
  382.         mov     ClippedWidth, ax    ; boundry.
  383.  
  384. m_NoClipRight:
  385.  
  386.         ; only this little bit here modded by AJR for XLIB addressing
  387.                                     ; Calculate starting video address in
  388.         mov     ax, DestY           ; VGA memory. This code sets DS to the
  389.         mov     bx, ScrnLogicalByteWidth ; (global var, needs DS intact)
  390.         mul     bx                  ; VGA segment, which is usually at
  391.         mov     di, ScrnOffs        ; 0a000
  392.         add     di, ax
  393.         mov     ax, SCREEN_SEG      ; We are going to set DS:DI to point
  394.         mov     ds, ax              ; to the start point in vid mem to start
  395.         mov     ax, DestX           ; drawing at.
  396.         mov     cx, ax              ; The offset DI is calculated by:
  397.         shr     ax, 2               ;     DI = Y*WIDTH+X/4 + offset
  398.         add     di, ax              ; DS:DI is ready!
  399.         ; end of mods
  400.  
  401.         mov     dx, SC_INDEX       ; Point the VGA Sequencer to the Map
  402.         mov     al, MAP_MASK        ; Mask register, so that we only need
  403.         out     dx, al              ; to send out 1 byte per column.
  404.  
  405.         inc     dx                  ; Move to the Sequencer's Data register.
  406.         and     cx, 3               ; Calculate the starting plane. This is
  407.         mov     al, 11h             ; just:
  408.         shl     al, cl              ; Plane =  (11h << (X AND 3))
  409.         out     dx, al              ; Select the first plane.
  410.  
  411.         ALIGN   2                   ; Since this point gets jumped to a lot,
  412.                                     ; make sure that it is DWORD aligned.
  413. m_RowLoop:
  414.         push    si                  ; Save the starting source index
  415.         push    di                  ; Save the starting dest index
  416.         push    ax                  ; Save the current plane mask
  417.         push    bp                  ; Save the current base pointer
  418.  
  419.         mov     ax, ClippedHeight   ; Use AL for row counter (0-239)
  420.         mov     bx, DecisionY       ; Use BX for decision variable
  421.         mov     cx, SourceWidth     ; Use CX for source width
  422.         mov     dx, SourceHeight    ; Use DX for source height * 2
  423.         shl     dx, 1
  424.         mov     bp, DestHeight      ; Use BP for dest height * 2
  425.         shl     bp, 1
  426.         mov     ah, es:[si]         ; Get the first source pixel
  427.  
  428.         ALIGN   2                   ; Common jump point... align for speed.
  429. m_ColumnLoop:
  430.         or      ah,ah               ; mask off 0 pixels a la Xlib
  431.         jz      m_no_draw
  432.         mov     ds:[di], ah         ; Draw a pixel
  433. m_no_draw:
  434.         dec     al                  ; Decrement line counter
  435.         jz      m_DoneWithCol       ; See if we're done with this column
  436.         add     di, 90              ; Go on to the next screen row
  437.         add     bx, dx              ; Increment the decision variable
  438.         js      m_ColumnLoop        ; Draw this source pixel again
  439.  
  440. m_IncSourceRow:
  441.         add     si, cx              ; Move to the next source pixel
  442.         sub     bx, bp              ; Decrement the decision variable
  443.         jns     m_IncSourceRow        ; See if we need to skip another source pixel
  444.         mov     ah, es:[si]         ; Get the next source pixel
  445.         jmp     m_ColumnLoop          ; Start drawing this pixel
  446.  
  447. m_DoneWithCol:
  448.         pop     bp                  ; Restore BP to access variables
  449.         pop     ax                  ; Restore AL = plane mask
  450.         pop     di                  ; Restore DI to top row of screen
  451.         pop     si                  ; Restore SI to top row of source bits
  452.  
  453.         rol     al, 1               ; Move to next plane
  454.         adc     di, 0               ; Go on to next screen column
  455.         mov     dx, SC_INDEX2       ; Tell the VGA what column we're in
  456.         out     dx, al              ; by updating the map mask register
  457.  
  458.         shl     cx, 1               ; CX = SourceWidth * 2
  459.         mov     bx, DecisionX       ; Use BX for the X decision variable
  460.         add     bx, cx              ; Increment the X decision variable
  461.         js      m_NextCol           ; Jump if we're still in the same source col.
  462.         mov     dx, DestWidth       ; DX = W * 2
  463.         shl     dx, 1
  464. m_IncSourceCol:
  465.         inc     si                  ; Move to next source column
  466.         sub     bx, dx              ; Decrement X decision variable
  467.         jns     m_IncSourceCol      ; See if we skip another source column
  468. m_NextCol:
  469.         mov     DecisionX, bx       ; Free up BX for ColLoop
  470.         dec     ClippedWidth        ; If we're not at last column
  471.         jnz     m_RowLoop           ;    then do another column
  472. m_Done:
  473.         pop   ds                          ; restore data segment
  474.         pop   di                          ; restore registers
  475.         pop   si
  476.  
  477.  
  478.         ret                         ; We're done!
  479.  
  480. xscale_masked  ENDP
  481.  
  482.  
  483.         END
  484.